/*----------------------------------------------------------------------------- 
GROUP:HoneyBadger
MEMBERS:Tyler Herzog, Chris Conry
DATE:February 2012
CS 4/55231 INTERNETENGINEERING 2012 SPRING
INSTRUCTOR: Javed Kahn
----------------------------------------------------------------------------- */
package server;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.util.Date;


public class ServerChild extends Thread {
	int id;
	Socket connection; // Socket which connection runs through
	OutputStream outStream; // an output stream
	DataOutputStream outDataStream; // stream used to send files/text
	BufferedReader inBuffer = null; // For reading in messages from client
	String inString, message, cName, HTTPURL; // strings to contain messages
	Server theServer;
	Boolean DisplayDetailedOutput = true, inChat = false, ourClient=false;  //for debug

	// /////HTTP STUFF//////
	String HTTP403 = "HTTP/1.1 403 \nContent-type: documenttype \nContent_length: 0 ",
			HTTP200 = "HTTP/1.1 200 ", HTTPACCEPT, HTTPMETHOD, HTTPVERSION;

	/*----------------------------------------------------------------------------- 
	 GROUP NAME: HoneyBadger
	 MODULE DEVELOPER 1. Tyler Herzog 2. Chris Conry
	 MODULE DESCRIPTION: 
	 Default constructor; establishes socket, id number, and server reference
	 ----------------------------------------------------------------------------- */
	ServerChild(Server server, int number, Socket connect) {
		theServer = server;
		connection = connect;
		id = number;
	}

	/*----------------------------------------------------------------------------- 
	 GROUP NAME: HoneyBadger
	 MODULE DEVELOPER 1. Tyler Herzog 2. Chris Conry
	 MODULE DESCRIPTION: 
	 Run function for the thread. When this thread is statred, this code executes.
	 Establishes the streams needed for in and out communication. This thread will
	 essentially do the "work" that the concurrent server needs handled. Once the
	 work is finished, this thread will die. Manages messages and HTTP protocol. 
	 Also handles specific command messages sent by client. 
	 ----------------------------------------------------------------------------- */
	public void run() {
		try {
			outStream = connection.getOutputStream();
			outDataStream = new DataOutputStream(outStream);
			inBuffer = new BufferedReader(new InputStreamReader(
					connection.getInputStream()));
			try {
				while (true) {
					try{
					// READ AND PROCESS INFORMATION FROM CLIENT//
					inChat = false;
					inString = inBuffer.readLine();
					HTTPMETHOD = inString.substring(0, inString.indexOf(" ")).toUpperCase().trim();
					HTTPURL = inString.substring(inString.indexOf(" "),inString.lastIndexOf(" ")).trim();
					HTTPVERSION = inString.substring(inString.lastIndexOf(" ")).trim();
					} catch (IndexOutOfBoundsException e) {}
					if (DisplayDetailedOutput) {
						System.out.println("Method: " + HTTPMETHOD + "\nURL: "
								+ HTTPURL + "\nVersion: " + HTTPVERSION);
					}
					while (inBuffer.ready()) {
						if (DisplayDetailedOutput) {
							System.out.println("Child-" + id+ ": message received: \"" + inString+ "\" ");
							theServer.log("Child-" + id+ ": message received: \"" + inString+ "\" ");
						}
						inString = inBuffer.readLine();
						if (inString.length() > 6) {
							if (inString.substring(0, 7).equals("Accept:")) {
								HTTPACCEPT = inString.substring(7);
							}
						}
					}
					//GET COMMAND ///////////////////////////////////////////////////////////////
					if (HTTPMETHOD.equals("GET")) {
						if (HTTPURL.equals("/")) {
							HTTPURL = "/index.html";
						}
						
						if (HTTPURL.equals("?")) {
                            // returns list of all file names
                            outDataStream.writeUTF("All files available on server: \n");
                            String filename;
                            
                            //For managing files on server
                            File folder = new File("serverfiles/");
                            File[] listOfFiles = folder.listFiles();

                            for (int i = 0; i < listOfFiles.length; i++) {
                                    if (listOfFiles[i].isFile()) {
                                            filename = listOfFiles[i].getName();
                                            outDataStream.writeUTF(filename + '\n');
                                    }
                            }
						} // end equals "?"
						
						
						File theFile = new File("serverfiles/" + HTTPURL.trim());
						if (!theFile.exists()) {
							System.out.println("Child-" + id+ ": The file requested does not exist");
							theServer.log("Child-" + id+ ": The file requested does not exist");
							outStream.write(HTTP403.getBytes());
						} else {
							// file does exist
								
							if(!ourClient){
								if (HTTPACCEPT.contains("text/html")) {
									outStream.write(HTTP200.getBytes());
									outStream.write(("Content_length: "
										+ theFile.length() + "\n").getBytes());
									String type = HTTPURL.trim().substring(HTTPURL.indexOf("."));
									outStream.write(("Content_type: "+type+"\n").getBytes());
								}
							}
							if(ourClient){
								outStream.write(HTTP200.getBytes());
								outStream.write(("Content_length: "
									+ theFile.length() + "\n").getBytes());
								String type = HTTPURL.trim().substring(HTTPURL.indexOf("."));
								outStream.write(("Content_type: "+type+"\n").getBytes());
							}
							
								SEND(theFile);
							
								System.out.println("Client-"+id+" Document "+HTTPURL.trim()+"of length "+theFile.length()+
									" has been retrieved and saved in a directory Cache");
								theServer.log("Client-"+id+" Document "+HTTPURL.trim()+"of length "+theFile.length()+
									" has been retrieved and saved in a directory Cache");
						}
					}
					//NAME COMMAND/////////////////////////////////////////////////////////////
					else if (HTTPMETHOD.equals("NAME")) {
						cName = HTTPVERSION;
						System.out.println(connection.getInetAddress()
								+ ": Client named: " + cName.trim());
						
						ourClient = true;
						HTTPACCEPT = "somethingLocal";
					} 
					//CHAT COMMAND/////////////////////////////////////////////////////////////
					else if (inString.substring(0, 4).toUpperCase().equals("CHAT")) {
						inChat = true;
						theServer.broadcast(id, cName + " has joined chat.");
						theServer.log(cName + " has joined chat.");
						message = inBuffer.readLine();
						while (!message.equals("/quit")) {
							try {
								theServer.broadcast(id, message);
								message = inBuffer.readLine();
							} catch (IOException e) {}
						}
					} 
					//DEATH COMMAND////////////////////////////////////////////////////////////

					else if (inString.substring(0, 5).toUpperCase().equals("DEATH")) {
						theServer.removeThread(id);
					}
				}
			} catch (IOException e) {
				// This happens when there is nothing else from client
				if (DisplayDetailedOutput) {
					System.out.println("Child-" + id + ": Done Listening to: "
							+ connection.getInetAddress());
				}
			} catch (IndexOutOfBoundsException e) {}
		} catch (IOException e1) {}
		// Thread has now finished, we need to close it's connection, and remove
		// from list on server
		System.out.println("Child-" + id
				+ ": has finished, and is closing connection to: "
				+ connection.getInetAddress());
		theServer.log("Child-" + id
				+ ": has finished, and is closing connection to: "
				+ connection.getInetAddress());
		close();
	}
////////////////////////////////////////////////////////////////////////////////////////////////
// FUNCTIONS BELOW
	
	/*----------------------------------------------------------------------------- 
	GROUP NAME: HoneyBadger
	MODULE DEVELOPER 1. Tyler Herzog 2. Chris Conry
	MODULE DESCRIPTION: 
	sends a message to the server, which is then broadcasted or the command is 
	handled. 
	----------------------------------------------------------------------------- */
	public void sendMessage(String str) {
		// sends a message to the server which then broadcasts
		try {
			outDataStream.writeUTF(str);
			outDataStream.flush();
		} catch (IOException e) {
			System.out.println("Child-" + id + ": Error sending message to: "
					+ connection.getInetAddress());
			theServer.log("Child-" + id + ": Error sending message to: "
					+ connection.getInetAddress());
		}
	}
////////////////////////////////////////////////////////////////////////////////////////////
	/*----------------------------------------------------------------------------- 
	GROUP NAME: HoneyBadger
	MODULE DEVELOPER 1. Tyler Herzog 2. Chris Conry
	MODULE DESCRIPTION: 
	closes connections with the client and kills the thread. 
	----------------------------------------------------------------------------- */
	public void close() {
		try {
			connection.close();
		} catch (IOException e) {
			System.out.println("Child-" + id
					+ ": IO exception when closing socket! ");
			theServer.log("Child-" + id
					+ ": IO exception when closing socket! ");
		}
		try { //sleep to allow ongoing deletions to occur before removing thred
			Thread.sleep(200);
		} catch (InterruptedException e) {}
		theServer.removeThread(id);
	}
////////////////////////////////////////////////////////////////////////////////////////////
	/*----------------------------------------------------------------------------- 
	GROUP NAME: HoneyBadger
	MODULE DEVELOPER 1. Tyler Herzog 2. Chris Conry
	MODULE DESCRIPTION: 
	getter for client name
	----------------------------------------------------------------------------- */
	public String getClientName() {
		return cName;
	}
////////////////////////////////////////////////////////////////////////////////////////////
	/*----------------------------------------------------------------------------- 
	GROUP NAME: HoneyBadger
	MODULE DEVELOPER 1. Tyler Herzog 2. Chris Conry
	MODULE DESCRIPTION: 
	getter for chatting boolean
	----------------------------------------------------------------------------- */
	public Boolean isChatting() {
		return inChat;
	}
////////////////////////////////////////////////////////////////////////////////////////////	
	/*----------------------------------------------------------------------------- 
	GROUP NAME: HoneyBadger
	MODULE DEVELOPER 1. Tyler Herzog 2. Chris Conry
	MODULE DESCRIPTION: 
	setter for ID number
	----------------------------------------------------------------------------- */
	public void setID(int newID){
		this.id = newID;
	}
////////////////////////////////////////////////////////////////////////////////////////////
	/*----------------------------------------------------------------------------- 
	GROUP NAME: HoneyBadger
	MODULE DEVELOPER 1. Tyler Herzog 2. Chris Conry
	MODULE DESCRIPTION: 
	Sends file. IT takes in a file object, creates the streams, and manages sending
	the file. Also manages looking for the #echo var="time"
	----------------------------------------------------------------------------- */
	public void SEND(File theFile) {
		FileInputStream stream;
		try {
			stream = new FileInputStream(theFile);
			if (HTTPACCEPT.contains("text/html")) {
				try {
					FileChannel fc = stream.getChannel();
					MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY,0, fc.size());
					message = Charset.defaultCharset().decode(bb).toString();
					if (message.contains("<!-#echo var=\"TIME\"->")) {
						long x = System.currentTimeMillis();
						Date resultdate = new Date(x);
						message = message.substring(0,
								message.indexOf("<!-#echo var=\"TIME\"->"))+ resultdate
								+ message.substring(message.lastIndexOf("<!-#echo var=\"TIME\"->") + 26);
					}
					outStream.write(message.getBytes());
				} finally {
					System.out.println("Child-" + id + ": closing stream ");
					stream.close();
					outStream.close();
				}
			} else {
				stream = new FileInputStream(theFile);
				byte[] bytes = new byte[16384];
				int ch = stream.read(bytes, 0, 16384);
				try {
					System.out.println("Child-" + id + ": Sending file: "+ HTTPURL);
					theServer.log("Child-" + id + ": Sending file: "+ HTTPURL);
					while (ch != -1) {
						outStream.write(bytes);
						ch = stream.read(bytes, 0, 16384);
					}
				} finally {
					System.out.println("Child-" + id + ": closing stream ");
					stream.close();
					outStream.close();
				}
			}
		} catch (FileNotFoundException e) {
			System.out.println("Child-" + id + ": FILENOTFOUNDEXCEPTION");
		} catch (IOException e) {
			System.out.println("Child-" + id + ": IOEXCEPTION! DEBUG!");
		}
	}
} // end of class NOTHING BEYOND

